﻿using GeneticAlgorithm.Demo.Common;
using GeneticAlgorithm.Demo.Common.Algorithm;
using GeneticAlgorithm.Demo.Common.Function;
using GeneticAlgorithm.Demo.Common.Function.UnaryFunction;
using Microsoft.VisualBasic.ApplicationServices;
using ScottPlot;
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Reflection;
using System.Security.AccessControl;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace GeneticAlgorithm.Demo
{
    public partial class FrmMain : Form
    {
        readonly Genetic _genetic;
        public FrmMain()
        {
            InitializeComponent();
            
            _genetic = new Genetic();
            RegisterType();
            List<IdValues> list = new List<IdValues>();
            foreach (var item in _functionType)
            {
                list.Add(new IdValues
                {
                    Id = item.Key.GetHashCode().ToString(),
                    Value = item.Value.Item1.DisplayText
                });
            }
            cmbTargetFunction.DataSource = list;
            cmbTargetFunction.DisplayMember= "Value";
            cmbTargetFunction.ValueMember= "Id";
            cmbTargetFunction.SelectedIndex = 0;

            this.dataGridView1.AutoGenerateColumns = false;
            this.dataGridView1.DataSource = _list;
        }
        List<int> _xs=new List<int>();
        private List<double> _ys=new List<double>();
        readonly ScottPlot.Color color = new ScottPlot.Color(255, 0, 0);
        BindingList<DisplayValue> _list= new BindingList<DisplayValue>();
        private void BindFitness(List<Gene> bestGenes)
        {
            formsPlot1.Plot.Clear();
            _xs.Clear();
            _ys.Clear();
            _list.Clear();
            for (int i = 0; i < bestGenes.Count; i++)
            {
                Gene obj = bestGenes[i];
                _xs.Add(i+1);
                _ys.Add(obj.Fitness);

                _list.Add(new DisplayValue
                {
                    Index = i+1,
                    Fitness = Math.Round(obj.Fitness, _genetic.Function.Accuracy),
                    X = Math.Round(obj.DisplayValue, _genetic.Function.Accuracy),
                    Y = Math.Round(obj.Y, _genetic.Function.Accuracy),
                });
            }

            dataGridView1.FirstDisplayedScrollingRowIndex = _list.Count - 1;

            var p = formsPlot1.Plot.Add.Scatter(_xs, _ys, color: color);
            p.LegendText = "适应度曲线";
            p.MarkerSize = 3;
            formsPlot1.Plot.Axes.SetLimitsX(0d, _xs.Count);
            formsPlot1.Plot.Font.Set("微软雅黑");
            formsPlot1.Plot.XLabel("序号");
            formsPlot1.Plot.YLabel("适应度");
            formsPlot1.Plot.Axes.SetLimitsY(0, 1.1d);
            formsPlot1.Plot.Legend.IsVisible = true;
            formsPlot1.Plot.ShowLegend(Alignment.LowerRight);
            formsPlot1.Refresh();
        }
        private void cmbTargetFunction_SelectedIndexChanged(object sender, EventArgs e)
        {
            IdValues? idValues = cmbTargetFunction.Items[cmbTargetFunction.SelectedIndex] as IdValues;
            if(!_functionType.ContainsKey((FunctionType)int.Parse(idValues.Id)))
            {
                MessageBox.Show($"不存在函数：{idValues.Value}");
                return;
            }
            Type type = _functionType[(FunctionType)int.Parse(idValues.Id)].Item2;
            IUnaryFunction ? unaryFunction = Activator.CreateInstance(type) as IUnaryFunction;
            _genetic.Function = unaryFunction;

            nudLower.Value = (decimal)unaryFunction.Range.lower;
            nudUpper.Value = (decimal)unaryFunction.Range.upper;
            nudAccuracy.Value = (decimal)unaryFunction.Accuracy;
            rbtnMax.Checked = unaryFunction.Solve == Common.Function.SolveType.Max;
            rbtnMin.Checked = !rbtnMax.Checked;

            BindFunction();
        }
        const int MarkerSize = 20;
        private void btnRun_Click(object sender, EventArgs e)
        {
            _genetic.Iterations = (uint)nudIterations.Value;
            _genetic.PopulationSize = (uint)nudPopulationSize.Value;
            _genetic.CrossoverRate = (double)nudCrossoverRate.Value;
            _genetic.MutationRate = (double)nudMutationRate.Value;

            _genetic.Function.Range = ((double)nudLower.Value, (double)nudUpper.Value);
            _genetic.Function.Accuracy = (byte)nudAccuracy.Value;
            _genetic.Function.Solve = rbtnMax.Checked ? Common.Function.SolveType.Max : Common.Function.SolveType.Min;
            BindFunction();
            var value = _genetic.Run();
            BindFitness(value);

            var gene = value.Last();
            var p2 = formsPlot2.Plot.Add.Scatter(new double[1] { gene.DisplayValue }, new double[1] { gene.Y }, color2);
            p2.MarkerSize = MarkerSize;
            formsPlot2.Refresh();
        }

        readonly ScottPlot.Color color2 = new ScottPlot.Color(0, 0, 255);
        private void BindFunction()
        {
            formsPlot2.Plot.Clear();

            List<double> xs = new List<double>();
            List<double> ys = new List<double>();
            int len = (int)nudDataCount.Value;
            double totalNumbers = _genetic.Function.Range.upper - _genetic.Function.Range.lower + 1; 
            double interval = totalNumbers / len;
            for (double i = 0; i < len; i++)
            {
                double x = i * interval+ _genetic.Function.Range.lower;
                xs.Add(x);
                ys.Add(_genetic.Function.Func(x));
            }
            var p = formsPlot2.Plot.Add.Scatter(xs, ys, color: color);
            p.LegendText = "函数曲线";
            p.MarkerSize = 3;
            formsPlot2.Plot.Axes.SetLimitsX(_genetic.Function.Range.lower, _genetic.Function.Range.upper);
            formsPlot2.Plot.Font.Set("微软雅黑");
            formsPlot2.Plot.XLabel("自变量");
            formsPlot2.Plot.YLabel("因变量");
            double step = 1 / totalNumbers* MarkerSize;
            formsPlot2.Plot.Axes.SetLimitsY(ys.Min() - step, ys.Max()+ step);
            formsPlot2.Plot.Legend.IsVisible = true;
            formsPlot2.Plot.ShowLegend(Alignment.LowerRight);
            formsPlot2.Refresh();
        }
        private Dictionary<FunctionType, (DisplayTextAttribute, Type)> _functionType;
        private void RegisterType()
        {
            Type attributeType = typeof(DisplayTextAttribute);
            Type functionType = typeof(IUnaryFunction);
            _functionType = RegisterType(functionType, attributeType);
        }
        private static Dictionary<FunctionType, (DisplayTextAttribute, Type)> RegisterType(Type baseType, Type attributeType) => RegisterType(Assembly.GetEntryAssembly(), baseType, attributeType);
        private static Dictionary<FunctionType, (DisplayTextAttribute, Type)> RegisterType(Assembly assembly, Type baseType, Type attributeType)
        {
            return assembly.GetTypes().Where(t => t.GetInterfaces().Contains(baseType) && t.GetCustomAttributes(attributeType, false).Any())
                .Select(t =>
                {
                    DisplayTextAttribute? sa = t.GetCustomAttribute(attributeType, false) as DisplayTextAttribute;

                    return (sa.Function, (sa, t));
                }).ToDictionary(t => t.Item1, t => t.Item2);
        }
    }
    public class DisplayValue
    {
        public int Index { get; set; }
        public double X { get; set; }
        public double Y { get; set; }
        public double Fitness {  get; set; }
    }
    public class IdValues
    {
        public string Id { get; set; }
        public string Value {  get; set; }
        public string Value2 { get; set; }
    }

}
